#pragma once
#include <iostream>
#include <algorithm>
#include <numeric>
#include <string>
#include <jsoncpp/json/json.h>
#include <nlohmann/json.hpp>
#include <sw/redis++/redis++.h>
#include "Log.hpp"
#include "index.hpp"

using namespace std;

namespace search_searcher
{
    struct InvertedElemPrint
    {
        uint64_t doc_id;
        int weight;
        vector<string> words;
        InvertedElemPrint() : doc_id(0), weight(0) {}
        InvertedElemPrint(int id, int wei, string tmp) : doc_id(id), weight(wei) { words.push_back(tmp); }
    };

    class Searcher
    {
    public:
        Searcher()
        {
        }
        ~Searcher()
        {
        }
        void InitSearcher(const string &input)
        {
            index = search_index::Index::GetInstance();
            index->BulidIndex(input);
        }
        void Read_AD_Messages(vector<string> *Addwords)
        {
            // 读取竞价配置文件，把网站标题和内容放到数组中
            ifstream inputFile("./data/Ad/Ad.txt");

            if (!inputFile)
            {
                cerr << "Error: Unable to open file.txt" << endl;
                return;
            }

            // 把读取到的信息放入到结构体中
            string line;
            while (getline(inputFile, line))
            {
                string title, content, url;
                int weight;
                stringstream iss(line);
                iss >> title >> content >> url >> weight;
                Addwords->push_back(title);
                Addwords->push_back(content);
            }

            inputFile.close();
        }
        // 把竞价的关键字加入到分词关键字当中
        void AddAdvertise(vector<string> *ResAddWords)
        {
            vector<string> Addwords;
            vector<string> CutAddWords;
            Read_AD_Messages(ResAddWords);
        }
        // 加信息
        void InsertAddContent(vector<InvertedElemPrint> *inverted_list_all)
        {
            ifstream inputFile("./data/Ad/Ad.txt");

            if (!inputFile)
            {
                cerr << "Error: Unable to open file.txt" << endl;
                return;
            }

            // 将读取到的信息放入到结构体中
            string line;
            int id = 0;
            while (getline(inputFile, line))
            {
                int weight = 0;
                string title, content, url;
                stringstream iss(line);
                iss >> title >> content >> url >> weight;
                title += "(广告)";
                inverted_list_all->push_back(InvertedElemPrint(id++, weight, title));
            }
        }

        // 进行前缀匹配
        void Prefix(const string& prefixword, string *json_string)
        {
            // 现在要根据prefixword构建出一个json串返回
            if(allletters(prefixword))
            {
                *json_string = Trie::getInstance().getPrefixMatchesJson(prefixword);
            }
            else 
            {
                lg(Warning, "字符有异常，不能进行构建，可能是恶意请求: %s", prefixword.c_str());
            }
        }

        // 返回热词统计的数据
        void HotWords(string *json_string)
        {
            // 可能会需要Docker进行部署，所以更新一个带Docker的解决方式
            const char* redisenv = getenv("REDIS_NAME");
            string redisconn = "tcp://";
            if(redisenv == nullptr)
                redisconn = redisconn + "127.0.0.1" + ":6379";
            else   
                redisconn = redisconn + redisenv + ":6379";
            
            sw::redis::Redis redis(redisconn);
            vector<pair<string, double>> hotWordsVector;
            auto it = back_inserter(hotWordsVector);
            redis.zrange("users:hotwords", 0, -1, it);

            Json::Value root;
            Json::Value hotWordsArray;

            // 将 hotwords_map 转换为 vector 并按词频降序排序
            sort(hotWordsVector.begin(), hotWordsVector.end(), [](const pair<string, double> &p1, const pair<string, double> &p2)
                 { return p1.second > p2.second; });

            for (const auto &entry : hotWordsVector)
            {
                Json::Value hotWordObject;
                hotWordObject["word"] = entry.first;
                hotWordObject["count"] = entry.second;
                hotWordsArray.append(hotWordObject);
            }

            root["hotWords"] = hotWordsArray;

            Json::StreamWriterBuilder builder;
            builder["indentation"] = "    ";
            unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
            stringstream json_stream;
            writer->write(root, &json_stream);

            *json_string = json_stream.str();
        }

        bool check(const string& query)
        {
            string word = query;
            transform(word.begin(), word.end(), word.begin(), ::tolower);
            for(auto e : word)
            {
                if(e < 'a' || e > 'z')
                    return false;
            }
            return true;
        }

        // 把当前传入进来的关键字放到Redis当中
        void AddToRedis(const string &query)
        {
            if(!check(query))
                return;
            // 可能会需要Docker进行部署，所以更新一个带Docker的解决方式
            const char* redisenv = getenv("REDIS_NAME");
            string redisconn = "tcp://";
            if(redisenv == nullptr)
                redisconn = redisconn + "127.0.0.1" + ":6379";
            else   
                redisconn = redisconn + redisenv + ":6379";
            sw::redis::Redis redis(redisconn);
            redis.zincrby("users:hotwords", 1, query);
        }

        // 传进来一个关键字，根据这个关键字构建结果
        void Search(const string &query, string *json_string)
        {
            // 进行一下热词统计，缓存到Redis中
            AddToRedis(query);
            // 0. 先去Redis里面看看有没有，如果就直接进行返回
            // 可能会需要Docker进行部署，所以更新一个带Docker的解决方式
            const char* redisenv = getenv("REDIS_NAME");
            string redisconn = "tcp://";
            if(redisenv == nullptr)
                redisconn = redisconn + "127.0.0.1" + ":6379";
            else   
                redisconn = redisconn + redisenv + ":6379";
            
            sw::redis::Redis redis(redisconn);
            auto result = redis.get(query);
            if(result)
            {
                *json_string = *result;
                return;
            }
            // 1. 把传入的query参数进行分词
            vector<string> words;
            search_utility::JiebaUtil::CutString(query, &words);

            // 2. 根据分词的结果，进行index查找
            // 建立所有词的倒排索引
            vector<InvertedElemPrint> inverted_list_all;
            // 根据id来进行合并去重
            unordered_map<uint64_t, InvertedElemPrint> tokens_map;

            for (auto word : words)
            {
                transform(word.begin(), word.end(), word.begin(), ::tolower);
                // 根据当前分词去寻找倒排索引
                vector<search_index::InvertedElem> *inverted_list = index->GetInvertedList(word);
                // 如果当前分词找不到结果，就跳过
                if (inverted_list == nullptr)
                {
                    cout << "当前分词: " << word << " 没有找到结果" << endl;
                    continue;
                }
                // 把当前词的倒排索引结果插入到汇总结果当中
                for (const auto &elem : *inverted_list)
                {
                    auto &item = tokens_map[elem.doc_id]; //[]:如果存在直接获取，如果不存在新建
                    // item一定是doc_id相同的print节点
                    item.doc_id = elem.doc_id;
                    item.weight += elem.weight;
                    item.words.push_back(elem.word);
                }
                cout << "当前分词: " << word << " 找到了结果" << endl;
            }

            for (const auto &item : tokens_map)
            {
                inverted_list_all.push_back(move(item.second));
            }

            // 把竞价信息直接加到inverted_list_all当中
            InsertAddContent(&inverted_list_all);

            // 3. 按照出现的权值排序，汇总结果
            sort(inverted_list_all.begin(), inverted_list_all.end(),
                 [](const InvertedElemPrint &e1, const InvertedElemPrint &e2)
                 {
                     return e1.weight > e2.weight;
                 });

            // 4. 构建json串
            Json::Value root;
            for (auto &item : inverted_list_all)
            {
                search_index::DocInfo *doc = index->GetForwardIndex(item.doc_id);
                if (nullptr == doc)
                    continue;
                Json::Value elem;
                elem["title"] = doc->title;
                elem["desc"] = GetDesc(doc->content, item.words[0]);
                elem["url"] = doc->url;
                // for deubg, for delete
                elem["id"] = (int)item.doc_id;
                elem["weight"] = item.weight; // int->string

                root.append(elem);
            }

            // Json::StyledWriter writer;
            Json::FastWriter writer;
            *json_string = writer.write(root);
            // 设置TTL为10分钟  
            std::chrono::milliseconds ttl(1 * 60 * 1000 * 10);
            redis.set(query, *json_string, ttl);

            if(allletters(query))
            {
                Trie& trie = Trie::getInstance();
                string jsonResult = trie.getPrefixMatchesJson(query);  
                cout << "匹配结果" << jsonResult << endl;
            }
        }

        bool allletters(const string& str)
        {
            for(auto& e : str)
                if(e < 'a' || e > 'z')
                    return false;
            return true;
        }

        string GetDesc(const string &html_content, const string &word)
        {
            if (html_content.size() < 150)
                return html_content;
            // 找到word在html_content中的首次出现，然后往前找50字节(如果没有，从begin开始)，往后找100字节(如果没有，到end就可以的)
            // 截取出这部分内容
            const int prev_step = 50;
            const int next_step = 100;
            // 1. 找到首次出现
            auto iter = search(html_content.begin(), html_content.end(), word.begin(), word.end(), [](int x, int y)
                               { return (tolower(x) == tolower(y)); });
            if (iter == html_content.end())
            {
                return "None1";
            }
            int pos = distance(html_content.begin(), iter);

            // 2. 获取start，end
            int start = 0;
            int end = html_content.size() - 1;
            // 如果之前有50+字符，就更新开始位置
            if (pos > start + prev_step)
                start = pos - prev_step;
            if (pos < end - next_step)
                end = pos + next_step;

            // 3. 截取子串,return
            if (start >= end)
                return "None2";
            string desc = html_content.substr(start, end - start);
            desc += "...";
            return desc;
        }

    private:
        // 提供查找的索引
        search_index::Index *index;
    };
}